import Router, { RouteConfig } from 'vue-router'
import { NProgress } from 'nprogress'
import { SubsystemConfig } from '@TG/interface/types/subsystem'
import { deepClone } from '..'
import { showMessage } from '../element/message'
import { getToken } from '../cookie'
import { getRouteConfigs, getRouteConfig } from '../router'
import { getSubsystemConfigs, getSubsystemConfig } from '../subsystem'
import { isEmpty, isNotEmpty } from '../validate'
import { userModule } from '@TG/store/modules/user'
import { appModule } from '@TG/store/modules/app'

// 转发路径需要单独处理
const redirect = '/redirect'
const guestWhiteList = [
  '/login'
]
const userWhiteList = [
  '/',
  '/login',
  '/401',
  '/404',
  '/dashboard'
]

/**
 * 构建子系统 key
 * @param id 子系统 id
 * @return 子系统 key
 */
export const buildSubsystemKey = (id: string) => id

/**
 * 构建角色 key
 * @param id 角色 id
 * @return 角色 key
 */
export const buildRoleKey = (id: string) => id

/**
 * 构建资源 key
 * @param type 资源类型
 * @param id 资源 id
 * @return 资源 key
 */
export const buildResourceKey = (type: 'route', id: string) => `${type}-${id}`

/**
 * 路由路径是否在白名单中
 * @param path 路径
 * @return 布尔值 [true 在白名单中 | false 不在白名单中]
 */
export const inWhiteList = (path: string) => {
  if (isNotEmpty(path)) {
    // 以转发路径开头的请求直接通过
    if (path.startsWith(redirect)) {
      return true
    } else {
      const routeConfig = getRouteConfig(path)
      const whiter: boolean = routeConfig?.meta?.whiter || false
      if (whiter) {
        return true
      } else {
        let whiteList = guestWhiteList
        if (isNotEmpty(getToken())) {
          whiteList = userWhiteList
        }
        return whiteList.includes(path)
      }
    }
  } else {
    return false
  }
}

/**
 * 是否有此权限
 * @param type 权限类型
 * @param key 权限 key，参考 buildSubsystemKey、buildRoleKey、buildResourceKey
 * @param permissions 权限集合
 * @return 布尔值 [true 有 | false 没有]
 * @see /directive/permission/index.js
 */
export const hasPermission = (type: 'subsystem' | 'role' | 'resource', key: string, permissions: Set<string>) => {
  let boole = false
  if (isNotEmpty(type) && isNotEmpty(key) && isNotEmpty(permissions)) {
    boole = permissions.has(key)
  } else {
    console.error('权限验证缺少关键参数，返回默认值 false')
  }
  return boole
}

/**
 * 根据白名单、权限集过滤 subsystemConfigs
 * @param subsystemConfigs 子系统配置集
 * @param subsystemKeys 子系统权限集合
 * @return 过滤后的子系统配置集
 */
export const filterSubsystemConfig = (subsystemConfigs: SubsystemConfig[], subsystemKeys: Set<string>) => {
  const filteredSubsystemConfigs: SubsystemConfig[] = []
  if (isNotEmpty(subsystemConfigs)) {
    subsystemConfigs.forEach(subsystemConfig => {
      const subsystemConfigClone: SubsystemConfig = deepClone(subsystemConfig)
      const id = subsystemConfigClone.id
      const whiter = subsystemConfigClone.whiter || false
      const children = subsystemConfigClone.children || []
      let boole = false
      if (whiter || hasPermission('subsystem', buildSubsystemKey(id), subsystemKeys)) {
        boole = true
      }
      if (boole) {
        if (isNotEmpty(children)) {
          subsystemConfigClone.children = filterSubsystemConfig(children, subsystemKeys)
        }
        filteredSubsystemConfigs.push(subsystemConfigClone)
      }
    })
  }
  return filteredSubsystemConfigs
}

/**
 * 根据子系统、白名单、权限集过滤 routeConfigs
 * @param routeConfigs 路由配置集
 * @param currentSubsystem 当前子系统
 * @param subsystemKeys 子系统权限集合
 * @param roleKeys 角色权限集合
 * @param resourceKeys 资源权限集合
 * @return 过滤后的子系统配置集
 */
export const filterRouteConfig = (
  routeConfigs: RouteConfig[],
  currentSubsystem: SubsystemConfig,
  subsystemKeys: Set<string>,
  roleKeys: Set<string>,
  resourceKeys: Set<string>
) => {
  const filteredRouteConfigs: RouteConfig[] = []
  if (isNotEmpty(routeConfigs)) {
    routeConfigs.forEach(routeConfig => {
      const routeConfigClone: RouteConfig = deepClone(routeConfig)
      const path = routeConfigClone.path
      const name = routeConfigClone.name || ''
      const subsystem: string = routeConfigClone.meta?.subsystem
      const whiter: boolean = routeConfigClone.meta?.whiter
      const roles: string[] = routeConfigClone.meta?.roles
      const children = routeConfigClone.children || []
      let bool = false
      if (
        isEmpty(subsystem) || (
          subsystem === currentSubsystem.id &&
          (currentSubsystem.whiter || hasPermission('subsystem', buildSubsystemKey(subsystem), subsystemKeys))
        )
      ) {
        if (whiter || inWhiteList(path) || hasPermission('resource', buildResourceKey('route', name), resourceKeys)) {
          bool = true
        } else if (isNotEmpty(roles)) {
          for (const role of roles) {
            if (hasPermission('role', buildRoleKey(role), roleKeys)) {
              bool = true
              break
            }
          }
        }
      }
      if (bool) {
        if (isNotEmpty(children)) {
          routeConfigClone.children = filterRouteConfig(children, currentSubsystem, subsystemKeys, roleKeys, resourceKeys)
        }
        filteredRouteConfigs.push(routeConfigClone)
      }
    })
  }
  return filteredRouteConfigs
}

/**
 * 添加路由器守卫（用于权限拦截）
 * @param router 路由器
 * @param progress 加载进度条
 */
export const addRouterEach = (router: Router, progress: NProgress) => {
  // 添加全局前置守卫
  router.beforeEach((to, from, next) => {
    progress.start()
    const userToken = userModule.token || getToken() // token 会存储在 Cookies 中
    const userId = userModule.id
    const path = to.path
    const name = to.name || to.fullPath
    const login = '/login'
    if (isEmpty(userToken)) {
      // 令牌为空，未登录
      if (inWhiteList(path)) {
        next() // path 在白名单中，确认导航
      } else {
        next(`${login}?redirect=${path}`) // path 不在白名单中，返回到登录页面
        progress.done() // 重新指定路由后，afterEach 不再执行，需要手工调用
      }
    } else {
      // 已登录
      if (path === login) {
        next({ path: '/' }) // 用户已登录，再次访问登录页，自动跳转到默认页
        progress.done() // 重新指定路由后，afterEach 不再执行，需要手工调用
      } else {
        /*
        检查用户信息是否已加载
        通常用户信息会在登录时进行加载
        考虑到登录后 F5 刷新页面可能导致登录信息丢失，因此将登录信息同步放在此出进行
        */
        const routeConfig: RouteConfig = getRouteConfig(name) as RouteConfig
        if (isEmpty(userId)) {
          userModule.sync().then(user => {
            const { subsystems, roles, resources } = user
            // 根据当前用户重置系统子系统、路由
            const subsystemConfigs = filterSubsystemConfig(getSubsystemConfigs(), new Set(subsystems))
            const subsystemId = routeConfig.meta?.subsystem
            const currentSubsystem = (isEmpty(subsystemId) ? subsystemConfigs[0] : getSubsystemConfig(subsystemId) as SubsystemConfig)
            const routeConfigs = filterRouteConfig(getRouteConfigs(), currentSubsystem, new Set(subsystems), new Set(roles), new Set(resources))
            appModule.setAccessedSubsystems(subsystemConfigs)
            appModule.setCurrentSubsystem(currentSubsystem)
            appModule.setAccessedRoutes(routeConfigs)
            next({ ...to, replace: true }) // 同步登录信息后重新访问当前页，以保证登录信息作用到页面上
          }).catch(error => {
            showMessage(error, 'error')
            userModule.clear()
            next(`${login}?redirect=${path}`) // 同步信息失败，返回到登录页面
            progress.done() // 重新指定路由后，afterEach 不再执行，需要手工调用
          })
        } else {
          // 登录信息已同步
          const whiter: boolean = routeConfig?.meta?.whiter || false
          const roles: string[] = routeConfig?.meta?.roles || []
          let bool = false
          if (whiter || inWhiteList(path) || hasPermission('resource', buildResourceKey('route', name), new Set(userModule.resources))) {
            bool = true
          } else if (isNotEmpty(roles)) {
            for (const role of roles) {
              if (hasPermission('role', buildRoleKey(role), new Set(userModule.roles))) {
                bool = true
                break
              }
            }
          }
          if (bool) {
            next()
          } else {
            next({ path: '/401', replace: true })
          }
        }
      }
    }
  })
  // 添加全局后置钩子
  router.afterEach((to, from) => {
    // 导航确认后（next）执行
    progress.done()
  })
}
